import std.concurrency, std.stdio, std.exception, std.variant;
import std.c.time;

import Definitions.msgCatalogue;
import Definitions.procNames;
import Registry.registry;

// In codec.c
extern (C) int lib_speex_init();
extern (C) int lib_speex_resample(short* smpls, int* in_sz, short* resmpls, int* out_sz);
extern (C) int lib_speex_get_frame_sz();
extern (C) int lib_speex_encode(short *samples, char* encoded_bytes);
// in stream.c
extern (C) int lib_shout_init();
extern (C) int lib_shout_open(char* samplerate);
extern (C) int lib_shout_close();
extern (C) int lib_shout_send(char* data, int len);

// Main processing loop for the streaming server
//===========================================================================================
Tid init_streaming() {
    // A streaming thread
    // Talks to IceCast

    auto streamingtid = spawn(&streamingproc);

    return streamingtid;
}

void streamingproc() {

    // Create the class that will read rx blocks from the device.
    writeln("Creating streaming server");
    auto ostreaming = new StreamingServer();
    ostreaming.process();
}

//===========================================================================================
class StreamingServer {

    // Constants
    immutable double output_scale = (2 ^^ 15) - 1.0;

    // Integer sample buffer
    short [] ismpls_buf;
    // Resample buffer
    short [] resmpls_buf;
    // Encoded stream from speex codec
    char [] encoded_stream;

    this() {
        // Allocate storage
        this.ismpls_buf = new short[1024];
        this.resmpls_buf = new short[1024];
        this.encoded_stream = new char[640];
    }

    void process() {

        bool running = true;
        int r;
        writeln("Streaming server starting");
        if((r=lib_speex_init()) == 0) {
            auto framesz = lib_speex_get_frame_sz();
            writeln("Speex frame size: ", framesz);
        }
        else {
            writeln("Streaming server failed to start - speex init failed (", r, ")");
            return;
        }
        if((r=lib_shout_init()) == 0) {
            char *rate = cast(char*)"32000";
            if((r=lib_shout_open(rate)) != 0) {
                writeln("Streaming server failed to start - libshout open failed  (", r, ")");
                return;
            }
        }
        else {
            writeln("Streaming server failed to start - libshout init failed (", r, ")");
            return;
        }

        while (running) {
            receive(
                // Sample output processing
                (M_SMPLS_OUT discriminator, immutable (float) [] smpls) {
                    //writeln("Streaming received data ", smpls);
                    // Resample the stream
                    int in_sz = 1024;
                    int out_sz = 1024;
                    // We work in integers not floats for this output stream
                    // Copy the floats into an interleaved integer buffer (16 bit shorts) and scale as we go
                    foreach(i, f; smpls) {
                        this.ismpls_buf[i] = cast(short)(output_scale*f);
                    }
                    //writeln("Data for encoding: ", this.ismpls_buf);
                    if(lib_speex_resample(ismpls_buf.ptr, &in_sz, this.resmpls_buf.ptr, &out_sz) == 0) {
                        // writeln("Resampled size is: ", out_sz);
                        // writeln("Resampled data: ", this.resmpls_buf);
                        // We have resampled data from 48KHz to 32KHz
                        // Add to the buffer

                        //  then send off as many framesz blocks as we can get out of the buffer
                        auto sz = lib_speex_encode(this.resmpls_buf.ptr, this.encoded_stream.ptr);
                        //writeln("Size written: ", sz);
                        //writeln("Buffer: ", this.encoded_stream);
                        if(sz > 0) {
                            // Now wrap in an OGG container

                            // Send to icecast via libshout
                            int r;
                            if((r = lib_shout_send(this.encoded_stream.ptr, sz)) != 0) {
                                writeln("Streaming server failed to send data: ", r);
                            }
                        }
                    }
                    else {
                        writeln("Streaming server failed to resample data");
                    }
                },
                // Wait for termination
                (M_TERM discriminator) {
                    lib_shout_close();
                    running = false;
                }
            );
        }
    }
}

